package com.gome.ocean.service.websql.impl;

import java.net.URL;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.HttpGet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSONArray;
import com.gome.framework.util.JsonUtil;
import com.gome.ocean.common.constants.WebSqlContants;
import com.gome.ocean.common.enums.DataBaseType;
import com.gome.ocean.common.enums.DataSource;
import com.gome.ocean.common.exception.CommonDataBaseException;
import com.gome.ocean.common.utils.DBSecurityUtil;
import com.gome.ocean.common.utils.HttpClientUtil;
import com.gome.ocean.common.utils.JdbcUtils;
import com.gome.ocean.dao.model.datasource.DataSourceBean;
import com.gome.ocean.dao.model.datasource.DatasourceBO;
import com.gome.ocean.dao.model.dsm.CommonDataBase;
import com.gome.ocean.dao.model.dsm.CommonDataBaseDetail;
import com.gome.ocean.dao.model.gddl.ProxyBO;
import com.gome.ocean.service.datax.DatasourceService;
import com.gome.ocean.service.gddl.ClusterService;
import com.gome.ocean.service.gddl.ProxyService;
import com.gome.ocean.service.task.LoadDatasourceJobTask;
import com.gome.ocean.service.websql.CommonDataSourceService;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.taobao.tddl.atom.config.TAtomDsConfDO;

@Service("commonDataSourceService")
public class CommonDataSourceServiceImpl implements CommonDataSourceService {

    private final Map<Integer, CommonDataBaseDetail> cacheDataBaseDetail = new ConcurrentHashMap<Integer, CommonDataBaseDetail>();
    private final static Logger                      LOG                 = LoggerFactory
        .getLogger(CommonDataSourceServiceImpl.class);

    @Autowired
    private ProxyService      proxyService;
    @Autowired
    private ClusterService    clusterService;
    @Autowired
    private DatasourceService datasourceService;

    @Override
    public Map<DataSource, Set<CommonDataBase>> getAllDataSource(List<String> projectNames) throws CommonDataBaseException {
        Map<DataSource, Set<CommonDataBase>> allDataBaseMaps = Maps.newHashMap();
        if (CollectionUtils.isEmpty(projectNames)) {
            return null;
        }
        Set<CommonDataBase> commonDataSources = Sets.newHashSet();
        // T_DATASOURCE
        List<DatasourceBO> dataSources = datasourceService.findDataSourcesByBeloneToCurrentUserProjects(projectNames);
        if (CollectionUtils.isNotEmpty(dataSources)) {
            for (DatasourceBO datasourceBO : dataSources) {
                CommonDataBase commonDataSource = new CommonDataBase();
                commonDataSource.setDataBaseId(datasourceBO.getId());
                if (DataBaseType.MySql == DataBaseType.getDataBaseType(datasourceBO.getSourceType())) {
                    commonDataSource.setDataBaseType(DataBaseType.MySql);
                    StringBuffer dbnames = new StringBuffer();
                    dbnames.append(datasourceBO.getUserName())
                        .append("@")
                        .append(StringUtils.substringBetween(datasourceBO.getJdbcUrl(), "//", "/"))
                        .append("@")
                        .append(StringUtils.substringAfterLast(datasourceBO.getJdbcUrl(), "/"));
                    commonDataSource.setDbName(dbnames.toString());
                    commonDataSource.setGname(datasourceBO.getProject());
                } else if (DataBaseType.Oracle == DataBaseType.getDataBaseType(datasourceBO.getSourceType())) {
                    commonDataSource.setDataBaseType(DataBaseType.Oracle);
                    StringBuffer dbnames = new StringBuffer();
                    if (datasourceBO.getJdbcUrl().contains("DESCRIPTION")) {
                        String ip = StringUtils
                            .substringAfter(StringUtils.substringBetween(datasourceBO.getJdbcUrl(), "HOST", ")"), "=")
                            .trim();
                        String port = StringUtils
                            .substringAfter(StringUtils.substringBetween(datasourceBO.getJdbcUrl(), "PORT", ")"), "=")
                            .trim();
                        String dbName = StringUtils
                            .substringAfter(
                                StringUtils.substringBetween(datasourceBO.getJdbcUrl(), "SERVICE_NAME", ")"), "=")
                            .trim();
                        dbnames.append(datasourceBO.getUserName())
                            .append("@")
                            .append(ip.concat(":").concat(port))
                            .append("@")
                            .append(dbName);
                    } else {
                        dbnames.append(datasourceBO.getUserName())
                            .append("@")
                            .append(StringUtils.substringAfter(datasourceBO.getJdbcUrl(), "@"));
                    }
                    commonDataSource.setDbName(dbnames.toString());
                    commonDataSource.setGname(datasourceBO.getProject());
                } else {
                    continue;
                }
                commonDataSource.setDataSource(DataSource.DATAX);
                commonDataSource.setSourceDescribe(StringUtils.EMPTY);
                commonDataSources.add(commonDataSource);
            }
        }
        // T_PROXY
        List<Long> clusterIds = clusterService.findAllClusterIdsByProjects(projectNames);
        if (CollectionUtils.isNotEmpty(clusterIds)) {
            List<ProxyBO> proxys = proxyService.findAllProxyByClusterIds(clusterIds);
            if (CollectionUtils.isNotEmpty(proxys)) {
                for (ProxyBO proxy : proxys) {
                    CommonDataBase commonDataSource = new CommonDataBase();
                    commonDataSource.setDataBaseId(proxy.getId());
                    commonDataSource.setDataBaseType(DataBaseType.DRDS);
                    commonDataSource.setDataSource(DataSource.DATAX);
                    commonDataSource.setSourceDescribe(StringUtils.EMPTY);
                    StringBuffer dbnames = new StringBuffer();
                    dbnames.append(proxy.getUser())
                        .append("@")
                        .append(proxy.getHost())
                        .append(":")
                        .append(proxy.getServerPort())
                        .append("@")
                        .append(proxy.getUser());
                    commonDataSource.setDbName(dbnames.toString());
                    commonDataSources.add(commonDataSource);
                    commonDataSource.setGname(proxy.getProject());
                }
            }
        }
        allDataBaseMaps.put(DataSource.DATAX, commonDataSources);
        return allDataBaseMaps;
    }

    @Override
    public CommonDataBaseDetail findDataBase(Long dataBaseId, Long dataSourceId, DataSource dataSource,
                                             DataBaseType dataBaseType) throws CommonDataBaseException {
        if (dataBaseId == null || dataBaseId.intValue() < 0) {
            return null;
        }
        if (DataSource.DSM == dataSource && dataSourceId == null || dataSourceId.intValue() < 0) {
            throw new CommonDataBaseException("不支持的数据库类型！");
        }
        if (DataSource.DATAX == dataSource) {
            int key = Math.abs(dataBaseType.hashCode() + dataBaseId.hashCode());
            if (cacheDataBaseDetail.containsKey(key)) {
                return cacheDataBaseDetail.get(key);
            }
            if (dataBaseType != null && DataBaseType.DRDS == dataBaseType) {
                ProxyBO proxyBO = proxyService.findProxyBOById(dataBaseId);
                if (proxyBO != null) {
                    CommonDataBaseDetail commonDataBaseDetail = new CommonDataBaseDetail();
                    commonDataBaseDetail.setIp(proxyBO.getHost());
                    commonDataBaseDetail.setPort(String.valueOf(proxyBO.getServerPort()));
                    commonDataBaseDetail.setJdbc(JdbcUtils.getMysqlJdbcUrl(proxyBO.getHost(),
                        String.valueOf(proxyBO.getServerPort()),
                        proxyBO.getUser()));
                    commonDataBaseDetail.setUser(proxyBO.getUser());
                    commonDataBaseDetail.setPwd(proxyBO.getPassword());
                    commonDataBaseDetail.setDataBaseType(DataBaseType.DRDS);
                    commonDataBaseDetail.setDataSource(createDruidDataSource(commonDataBaseDetail));
                    commonDataBaseDetail.setDbName(proxyBO.getUser());
                    cacheDataBaseDetail.put(key, commonDataBaseDetail);
                    return commonDataBaseDetail;
                }
                return null;
            } else {
                DatasourceBO datasource = datasourceService.findDatasourceBOById(dataBaseId);
                if (datasource != null) {
                    CommonDataBaseDetail commonDataBaseDetail = new CommonDataBaseDetail();
                    commonDataBaseDetail.setJdbc(datasource.getJdbcUrl());
                    commonDataBaseDetail.setUser(datasource.getUserName());
                    commonDataBaseDetail.setPwd(datasource.getPassword());
                    DataBaseType baseType = DataBaseType.getDataBaseType(datasource.getSourceType());
                    if (DataBaseType.Oracle == baseType) {
                        if (datasource.getJdbcUrl().contains("DESCRIPTION")) {
                            String ip = StringUtils
                                .substringAfter(StringUtils.substringBetween(datasource.getJdbcUrl(), "HOST", ")"), "=")
                                .trim();
                            String port = StringUtils
                                .substringAfter(StringUtils.substringBetween(datasource.getJdbcUrl(), "PORT", ")"), "=")
                                .trim();
                            String dbName = StringUtils
                                .substringAfter(
                                    StringUtils.substringBetween(datasource.getJdbcUrl(), "SERVICE_NAME", ")"), "=")
                                .trim();
                            commonDataBaseDetail.setIp(ip);
                            commonDataBaseDetail.setPort(port);
                            commonDataBaseDetail.setDbName(dbName);
                        } else {
                            String[] ipPorts = StringUtils
                                .split(StringUtils.substringAfter(datasource.getJdbcUrl(), "@"), ":");
                            commonDataBaseDetail.setIp(ipPorts[0]);
                            commonDataBaseDetail.setPort(ipPorts[1]);
                            commonDataBaseDetail.setDbName(datasource.getUserName());
                        }
                    } else {
                        String[] ipPorts = StringUtils
                            .split(StringUtils.substringBetween(datasource.getJdbcUrl(), "//", "/"), ":");
                        String dbName = StringUtils.substringAfterLast(datasource.getJdbcUrl(), "/");
                        commonDataBaseDetail.setIp(ipPorts[0]);
                        commonDataBaseDetail.setPort(ipPorts[1]);
                        commonDataBaseDetail.setDbName(dbName);
                    }
                    commonDataBaseDetail.setDataBaseType(baseType);
                    commonDataBaseDetail.setDataSource(createDruidDataSource(commonDataBaseDetail));
                    cacheDataBaseDetail.put(key, commonDataBaseDetail);
                    return commonDataBaseDetail;
                }
                return null;
            }
        }
        throw new CommonDataBaseException("不支持的数据库类型！");
    }

    private DruidDataSource createDruidDataSource(CommonDataBaseDetail commonDataBaseDetail) {
        DruidDataSource localDruidDataSource = new DruidDataSource();
        localDruidDataSource.setTestOnBorrow(false);
        localDruidDataSource.setTestWhileIdle(true);
        localDruidDataSource.setUsername(commonDataBaseDetail.getUser());
        localDruidDataSource.setPassword(commonDataBaseDetail.getPwd());
        if (DataBaseType.Oracle == commonDataBaseDetail.getDataBaseType()) {
            localDruidDataSource.setDriverClassName(DataBaseType.Oracle.getDriverClassName());
            localDruidDataSource.setUrl(commonDataBaseDetail.getJdbc());
            localDruidDataSource.setDbType(DataBaseType.Oracle.getTypeFullName());
        } else {
            localDruidDataSource.setDriverClassName(DataBaseType.MySql.getDriverClassName());
            localDruidDataSource.setUrl(commonDataBaseDetail.getJdbc());
            localDruidDataSource.setDbType(DataBaseType.MySql.getTypeFullName());
        }
        // lazy init 先设置为0 后续真正执行时才创建连接
        localDruidDataSource.setInitialSize(TAtomDsConfDO.defaultInitPoolSize);
        localDruidDataSource.setMinIdle(LoadDatasourceJobTask.DEFAULT_POOL_SIZE);
        localDruidDataSource.setMaxActive(LoadDatasourceJobTask.DEFAULT_MAX_POOL_SIZE);
        // 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒
        localDruidDataSource.setTimeBetweenEvictionRunsMillis(15 * 60 * 1000);
        // 配置一个连接在池中最小生存的时间，单位是毫秒
        localDruidDataSource.setMinEvictableIdleTimeMillis(15 * 60 * 1000);
        localDruidDataSource.setMaxWait(TAtomDsConfDO.defaultMaxWait);
        return localDruidDataSource;
    }

    @Override
    public List<CommonDataBase> getDataBaseListsByHttp(List<CommonDataBase> list, String email, String ip,
                                                       String envType) {

        Map<String, Object> param = Maps.newLinkedHashMap();
        param.put("ptype", WebSqlContants.GROUP_DB);
        param.put("email", email);
        param.put("env_type", envType);
        param.put("gtype", WebSqlContants.GROUP_TYPE_DEV);

        HttpClientUtil clientUtil = new HttpClientUtil();
        HttpGet httpGet = HttpClientUtil.getGetRequest();
        String path;
        Set<DataSourceBean> newList = null;
        try {
            path = WebSqlContants.getFormatAuthorityGroupPathStr(ip,
                URLEncoder.encode(JsonUtil.toJson(param), "UTF-8"));
            URL url = new URL(path);
            httpGet.setURI(url.toURI());
            String json = clientUtil.executeAndGetWithFailedRetry(httpGet, 6, 10001);
            List<DataSourceBean> datasourceLists = JSONArray.parseArray(json, DataSourceBean.class);
            newList = new HashSet<DataSourceBean>();  
            newList.addAll(datasourceLists);
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }

        if (newList != null && newList.size() > 0) {
            for (DataSourceBean datasource : newList) {
                CommonDataBase commonDataBase = new CommonDataBase();
                commonDataBase.setDataBaseId(datasource.getAdid());
                StringBuffer dbnames = new StringBuffer();
                if (DataBaseType.MySql == DataBaseType.getDataBaseType(datasource.getDb_type().toLowerCase())) {
                    commonDataBase.setDataBaseType(DataBaseType.MySql);
                    dbnames.append(datasource.getDb_user())
                        .append("@")
                        .append(datasource.getIpaddr())
                        .append(":")
                        .append(datasource.getDb_port())
                        .append("@")
                        .append(datasource.getDb_name());
                    commonDataBase.setGname(StringUtils.isEmpty(datasource.getGname()) ? datasource.getGname_cn() : datasource.getGname());
                } else if (DataBaseType.Oracle == DataBaseType.getDataBaseType(datasource.getDb_type().toLowerCase())) {
                    commonDataBase.setDataBaseType(DataBaseType.Oracle);
                    dbnames.append(datasource.getDb_user())
                        .append("@")
                        .append(datasource.getIpaddr())
                        .append(":")
                        .append(datasource.getDb_port())
                        .append("/")
                        .append(datasource.getDb_name());
                }
                commonDataBase.setDbName(dbnames.toString());
                commonDataBase.setDataSourceId(datasource.getGuid());
                commonDataBase.setGname(StringUtils.isEmpty(datasource.getGdept()) ? datasource.getGname() : datasource.getGdept());
                list.add(commonDataBase);
            }
        }

        return list;
    }

    /**
     * 在Oracle中 数据库库名为用户名
     */
    @Override
    public CommonDataBaseDetail getSingleDataBaseByHttp(Long dataSourceId, DataBaseType dataBaseType, String email,
                                                        String ip, String envType) {
        if (dataSourceId == null || dataSourceId.longValue() < 0) {
            return null;
        }
        int key = Math.abs(dataBaseType.hashCode() + dataSourceId.hashCode());
        if (cacheDataBaseDetail.containsKey(key)) {
            return cacheDataBaseDetail.get(key);
        }

        CommonDataBaseDetail commonDataBaseDetail = new CommonDataBaseDetail();

        Map<String, Object> param = Maps.newLinkedHashMap();
        param.put("ptype", WebSqlContants.GROUP_DB);
        param.put("email", email);
        param.put("guid", dataSourceId);
        param.put("env_type", envType);
        param.put("gtype", WebSqlContants.GROUP_TYPE_DEV);

        HttpClientUtil clientUtil = new HttpClientUtil();
        HttpGet httpGet = HttpClientUtil.getGetRequest();
        String path;
        try {
            path = WebSqlContants.getFormatAuthorityGroupPathStr(ip,
                URLEncoder.encode(JsonUtil.toJson(param), "UTF-8"));
            URL url = new URL(path);
            httpGet.setURI(url.toURI());
            String json = clientUtil.executeAndGetWithFailedRetry(httpGet, 6, 10001);
            DataSourceBean datasource = JSONArray.parseArray(json, DataSourceBean.class).get(0);
            if (datasource != null) {
                commonDataBaseDetail.setIp(datasource.getIpaddr());
                commonDataBaseDetail.setPort(datasource.getDb_port().toString());
                if (DataBaseType.MySql == dataBaseType) {
                    commonDataBaseDetail.setJdbc(JdbcUtils.getMysqlJdbcUrl(datasource.getIpaddr(),
                        datasource.getDb_port().toString(),
                        datasource.getDb_name()));
                    commonDataBaseDetail.setDbName(datasource.getDb_name());
                } else {
                    commonDataBaseDetail.setJdbc(JdbcUtils.getOracleJdbcUrl2(datasource.getIpaddr(),
                        datasource.getDb_port().toString(),
                        datasource.getDb_name()));
                    commonDataBaseDetail.setDbName(datasource.getDb_user());
                }
                commonDataBaseDetail.setUser(datasource.getDb_user());
                commonDataBaseDetail
                    .setPwd(DBSecurityUtil.decrypt(datasource.getPassword(), WebSqlContants.GLOBAL_DECRYPT_P));
                commonDataBaseDetail.setDataBaseType(dataBaseType);
                commonDataBaseDetail.setDataSource(createDruidDataSource(commonDataBaseDetail));

                cacheDataBaseDetail.put(key, commonDataBaseDetail);
                return commonDataBaseDetail;
            }
            return null;
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
        return null;
    }

}
